package main

import (
	"log"
	"time"
)

var LimitQueue map[string][]int64

func main() {
	// 测试模拟接口调用
	ipStr := "ip: 127.0.0.1" // 接口调用标识
	var nums uint64 = 5 // 窗口数
	var times int64 = 10 // 窗口限流时间
	for i := 0; i < 30; i++ {
		if SlidingWindowLimit(ipStr, nums, times) {
			log.Println("接口可以正常的调用...")
			log.Println(LimitQueue)
			time.Sleep(time.Second)
		} else {
			log.Println("接口已经达到了最大限流值，限流中···")
			time.Sleep(time.Second)
		}
	}
	time.Sleep(time.Duration(30) * time.Second)
}

// SlidingWindowLimit 滑动窗口计数器限流
func SlidingWindowLimit(queueName string, windowNums uint64, times int64) bool {
	// 必传参数
	if queueName == "" || windowNums <= 0 || times <= 0 {
		return false
	}
	// 如果限流map为nil，则先申明内存空间
	if LimitQueue == nil {
		LimitQueue = make(map[string][]int64)
	}
	// 如果当前调用者的限流队列为nil，则先申明内存空间
	if LimitQueue[queueName] == nil {
		LimitQueue[queueName] = make([]int64, 0)
	}
	timeNow := time.Now().Unix()
	// 判断窗口数是否满了，如果没满则向切片中添加数据
	if uint64(len(LimitQueue[queueName])) < windowNums {
		LimitQueue[queueName] = append(LimitQueue[queueName], timeNow)
		return true
	}
	// 窗口满了取最早的数据出来
	earliestTime := LimitQueue[queueName][0]
	// 判断当前时间差是否大于了窗口限流时间， 如果大于则将当前时间替换最老的时间(总窗口数不能变，窗口从左到右移动一个)
	if timeNow - earliestTime > times {
		LimitQueue[queueName] = LimitQueue[queueName][1:]
		LimitQueue[queueName] = append(LimitQueue[queueName], timeNow)
	} else { // 限流
		return false
	}
	return true
}